#pragma once
#include <windows.h>
#include <stdio.h>
#include <d3d9.h>
#pragma comment(lib,"d3d9.lib")

#include "Texture.h"

// #define cc_use_d3dx
// #define cc_use_d3dx_mat

#ifdef cc_use_d3dx
#include "dx9_2004/include/d3dx9.h"
#pragma comment(lib,"dx9_2004/lib/x86/d3dx9.lib")
#elif defined(cc_use_d3dx_mat)
#include "dx9_2004/include/d3dx9.h"
#pragma comment(lib,"dx9_2004/lib/x86/d3dx9.lib")
// #include "DirectXMath.h"
// #include "DirectXMathMatrix.inl"
#endif

#ifndef cc_use_d3dx_mat
inline void D3DXMatrixIdentity(D3DMATRIX* pOut) {
    // XMMatrixIdentity
    pOut->m[0][1] = pOut->m[0][2] = pOut->m[0][3] =
        pOut->m[1][0] = pOut->m[1][2] = pOut->m[1][3] =
        pOut->m[2][0] = pOut->m[2][1] = pOut->m[2][3] =
        pOut->m[3][0] = pOut->m[3][1] = pOut->m[3][2] = 0.0f;

    pOut->m[0][0] = pOut->m[1][1] = pOut->m[2][2] = pOut->m[3][3] = 1.0f;
}

inline void D3DXMatrixScaling(D3DMATRIX* _matProj, float ScaleX, float ScaleY, float ScaleZ) {
	// XMMatrixScaling
	auto& M = *_matProj;
    M.m[0][0] = ScaleX;
    M.m[0][1] = 0.0f;
    M.m[0][2] = 0.0f;
    M.m[0][3] = 0.0f;

    M.m[1][0] = 0.0f;
    M.m[1][1] = ScaleY;
    M.m[1][2] = 0.0f;
    M.m[1][3] = 0.0f;

    M.m[2][0] = 0.0f;
    M.m[2][1] = 0.0f;
    M.m[2][2] = ScaleZ;
    M.m[2][3] = 0.0f;

    M.m[3][0] = 0.0f;
    M.m[3][1] = 0.0f;
    M.m[3][2] = 0.0f;
    M.m[3][3] = 1.0f;
}


inline void D3DXMatrixTranslation(D3DMATRIX* tmp, float OffsetX, float OffsetY, float OffsetZ) {
	// XMMatrixTranslation
	auto& M = *tmp;
    M.m[0][0] = 1.0f;
    M.m[0][1] = 0.0f;
    M.m[0][2] = 0.0f;
    M.m[0][3] = 0.0f;

    M.m[1][0] = 0.0f;
    M.m[1][1] = 1.0f;
    M.m[1][2] = 0.0f;
    M.m[1][3] = 0.0f;

    M.m[2][0] = 0.0f;
    M.m[2][1] = 0.0f;
    M.m[2][2] = 1.0f;
    M.m[2][3] = 0.0f;

    M.m[3][0] = OffsetX;
    M.m[3][1] = OffsetY;
    M.m[3][2] = OffsetZ;
    M.m[3][3] = 1.0f;
}
inline void D3DXMatrixMultiply(D3DMATRIX* tmp, D3DMATRIX* _matProj, D3DMATRIX* _matProj2) {
	// XMMatrixMultiply
	const auto& M1 = *_matProj;
	const auto& M2 = *_matProj2;
	auto& mResult = *tmp;
    // Cache the invariants in registers
    float x = M1.m[0][0];
    float y = M1.m[0][1];
    float z = M1.m[0][2];
    float w = M1.m[0][3];
    // Perform the operation on the first row
    mResult.m[0][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
    mResult.m[0][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
    mResult.m[0][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
    mResult.m[0][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
    // Repeat for all the other rows
    x = M1.m[1][0];
    y = M1.m[1][1];
    z = M1.m[1][2];
    w = M1.m[1][3];
    mResult.m[1][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
    mResult.m[1][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
    mResult.m[1][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
    mResult.m[1][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
    x = M1.m[2][0];
    y = M1.m[2][1];
    z = M1.m[2][2];
    w = M1.m[2][3];
    mResult.m[2][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
    mResult.m[2][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
    mResult.m[2][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
    mResult.m[2][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
    x = M1.m[3][0];
    y = M1.m[3][1];
    z = M1.m[3][2];
    w = M1.m[3][3];
    mResult.m[3][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
    mResult.m[3][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
    mResult.m[3][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
    mResult.m[3][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
}


inline void D3DXMatrixOrthoOffCenterLH (D3DMATRIX* tmp,
	float ViewLeft,
	float ViewRight,
	float ViewBottom,
	float ViewTop,
	float NearZ,
	float FarZ
) {
	//     assert(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001f));
	//     assert(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001f));
	//     assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));

	float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft);
	float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom);
	float fRange = 1.0f / (FarZ - NearZ);

	auto& M = *tmp;
	M.m[0][0] = ReciprocalWidth + ReciprocalWidth;
	M.m[0][1] = 0.0f;
	M.m[0][2] = 0.0f;
	M.m[0][3] = 0.0f;

	M.m[1][0] = 0.0f;
	M.m[1][1] = ReciprocalHeight + ReciprocalHeight;
	M.m[1][2] = 0.0f;
	M.m[1][3] = 0.0f;

	M.m[2][0] = 0.0f;
	M.m[2][1] = 0.0f;
	M.m[2][2] = fRange;
	M.m[2][3] = 0.0f;

	M.m[3][0] = -(ViewLeft + ViewRight) * ReciprocalWidth;
	M.m[3][1] = -(ViewTop + ViewBottom) * ReciprocalHeight;
	M.m[3][2] = -fRange * NearZ;
	M.m[3][3] = 1.0f;
}

#endif

#ifdef MYDLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
#define CALL __stdcall

#define PI (3.1415926536f)
#define my2d my2d_my2d
#define MY_INLINE inline
#define MY_DELETE(p) do{if(p != 0){delete p; p = 0;}}while(0)
#define MY_DELETE_ARR(p) do{if(p != 0){delete[] p; p = 0;}}while(0)
#define MY_DELETE_AR2(p,n)if (p != 0){for (int i = 0; i < n; ++i)delete[] p[i];p = 0;}

#define LOWORDINT(n) ((int)((signed short)(LOWORD(n))))
#define HIWORDINT(n) ((int)((signed short)(HIWORD(n))))
#define D3DFVF_HGEVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)

static const int
K_LBUTTON = 0x01,
K_RBUTTON = 0x02,
K_MBUTTON = 0x04,

K_ESCAPE = 0x1B,
K_BACKSPACE = 0x08,
K_TAB = 0x09,
K_ENTER = 0x0D,
K_SPACE = 0x20,
K_SHIFT = 0x10,
K_CTRL = 0x11,
K_ALT = 0x12,
K_LWIN = 0x5B,
K_RWIN = 0x5C,
K_APPS = 0x5D,
K_PAUSE = 0x13,
K_CAPSLOCK = 0x14,
K_NUMLOCK = 0x90,
K_SCROLLLOCK = 0x91,
K_PGUP = 0x21,
K_PGDN = 0x22,
K_HOME = 0x24,
K_END = 0x23,
K_INSERT = 0x2D,
K_DELETE_ = 0x2E,
K_LEFT = 0x25,
K_UP = 0x26,
K_RIGHT = 0x27,
K_DOWN = 0x28,

K_0 = 0x30,
K_1 = 0x31,
K_2 = 0x32,
K_3 = 0x33,
K_4 = 0x34,
K_5 = 0x35,
K_6 = 0x36,
K_7 = 0x37,
K_8 = 0x38,
K_9 = 0x39,

K_A = 0x41,
K_B = 0x42,
K_C = 0x43,
K_D = 0x44,
K_E = 0x45,
K_F = 0x46,
K_G = 0x47,
K_H = 0x48,
K_I = 0x49,
K_J = 0x4A,
K_K = 0x4B,
K_L = 0x4C,
K_M = 0x4D,
K_N = 0x4E,
K_O = 0x4F,
K_P = 0x50,
K_Q = 0x51,
K_R = 0x52,
K_S = 0x53,
K_T = 0x54,
K_U = 0x55,
K_V = 0x56,
K_W = 0x57,
K_X = 0x58,
K_Y = 0x59,
K_Z = 0x5A,

K_GRAVE = 0xC0,
K_MINUS = 0xBD,
K_EQUALS = 0xBB,
K_BACKSLASH = 0xDC,
K_LBRACKET = 0xDB,
K_RBRACKET = 0xDD,
K_SEMICOLON = 0xBA,
K_APOSTROPHE = 0xDE,
K_COMMA = 0xBC,
K_PERIOD = 0xBE,
K_SLASH = 0xBF,

K_NUMPAD0 = 0x60,
K_NUMPAD1 = 0x61,
K_NUMPAD2 = 0x62,
K_NUMPAD3 = 0x63,
K_NUMPAD4 = 0x64,
K_NUMPAD5 = 0x65,
K_NUMPAD6 = 0x66,
K_NUMPAD7 = 0x67,
K_NUMPAD8 = 0x68,
K_NUMPAD9 = 0x69,

K_MULTIPLY = 0x6A,
K_DIVIDE = 0x6F,
K_ADD = 0x6B,
K_SUBTRACT = 0x6D,
K_DECIMAL = 0x6E,

K_F1 = 0x70,
K_F2 = 0x71,
K_F3 = 0x72,
K_F4 = 0x73,
K_F5 = 0x74,
K_F6 = 0x75,
K_F7 = 0x76,
K_F8 = 0x77,
K_F9 = 0x78,
K_F10 = 0x79,
K_F11 = 0x7A,
K_F12 = 0x7B;

static const char* WINDOW_CLASS_NAME = "MY__WNDCLASS";
static int _seed = 0;
static const char* MY_SECTION_NAME = "Default";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

typedef bool(*MyCallback)();
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned short ushort;
typedef unsigned char uchar;

typedef ulong TEXTURE;
typedef ulong Target;

enum PRIM { LINE = 2, TRIPLE, QUAD };

enum BLEND { CA = 1, CM = 0, A = 2, AA = 0, Z = 4, _Z = 0, D = (CM | A | _Z), D_Z = (CM | A | Z) };

enum KEYTYPE { DOWN = 1, UP, MDOWN, MUP, MOVE, WHEEL };

enum KEYFLAG { SHIFT = 1, CTRL = 2, ALT = 4, CAPSLOCK = 8, SCROLLLOCK = 16, NUMLOCK = 32, REPEAT = 64 };

// struct Vertex {
// 	float x, y;		// screen position    
// 	float z = 0.5f;		// Z-buffer depth 0..1
// 	DWORD color = 0xFFFFFFFF;	// color
// 	float tx, ty;	// texture coordinates
// };
// 
// 
// struct Vertex4 {
//     Vertex vertexs[4/*PRIM::QUAD*/];
// };

// struct RenderTargetList {
// 	int width, height;
// 	IDirect3DTexture9* pTex;
// 	IDirect3DSurface9* pDepth;
// 	RenderTargetList* next;
// };

struct TextureList {
	IDirect3DTexture9* pTex;
	int	width;
	int	height;
	TextureList* next;
};

using RenderTargetList = TextureList;

struct InputEvent { int type, key, flag, chr, wheel; float x, y; };

struct InputEventList { InputEvent event; InputEventList* next; };

static const char* _keyNames[] =
{
	"?",
	"Left Mouse Button", "Right Mouse Button", "?", "Middle Mouse Button",
	"?", "?", "?", "Backspace", "Tab", "?", "?", "?", "Enter", "?", "?",
	"Shift", "Ctrl", "Alt", "Pause", "Caps Lock", "?", "?", "?", "?", "?", "?",
	"Escape", "?", "?", "?", "?",
	"Space", "Page Up", "Page Down", "End", "Home",
	"Left Arrow", "Up Arrow", "Right Arrow", "Down Arrow",
	"?", "?", "?", "?", "Insert", "Delete", "?",
	"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
	"?", "?", "?", "?", "?", "?", "?",
	"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
	"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
	"Left Win", "Right Win", "Application", "?", "?",
	"NumPad 0", "NumPad 1", "NumPad 2", "NumPad 3", "NumPad 4",
	"NumPad 5", "NumPad 6", "NumPad 7", "NumPad 8", "NumPad 9",
	"Multiply", "Add", "?", "Subtract", "Decimal", "Divide",
	"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"Num Lock", "Scroll Lock",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"Semicolon", "Equals", "Comma", "Minus", "Period", "Slash", "Grave",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?",
	"Left bracket", "Backslash", "Right bracket", "Apostrophe",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
	"?", "?", "?"
};

struct My2d {
	virtual void CALL release() = 0;
	virtual bool CALL init() = 0;
	virtual void CALL shutdown() = 0;
	virtual bool CALL start() = 0;
	virtual void CALL setWinMode(bool value) = 0;
	virtual void CALL setZbuffer(bool value) = 0;
	virtual void CALL setTextureFilter(bool value) = 0;
	virtual void CALL setHideMouse(bool value) = 0;
	virtual void CALL setSuspend(bool value) = 0;
	virtual void CALL setFrameFunc(MyCallback value) = 0;
	virtual void CALL setRenderFunc(MyCallback value) = 0;
	virtual void CALL setFocusGainFunc(MyCallback value) = 0;
	virtual void CALL setFocusLostFunc(MyCallback value) = 0;
	virtual void CALL setGfxRestoreFunc(MyCallback value) = 0;
	virtual void CALL setExitFunc(MyCallback value) = 0;
	virtual void CALL setHwnd(HWND value) = 0;
	virtual void CALL setScreenWidth(int value) = 0;
	virtual void CALL setScreenHeight(int value) = 0;
	// virtual void CALL setScreenBPP(int value) = 0;
	virtual void CALL setFPS(int value) = 0;
	virtual void CALL setIcon(const char* value) = 0;
	virtual void CALL setTitle(const char* value) = 0;
	virtual void CALL setIniFile(const char* value) = 0;
	virtual void CALL setLogFile(const char* value) = 0;
	virtual bool CALL getWinMode() = 0;
	virtual bool CALL getZbuffer() = 0;
	virtual bool CALL getTextureFilter() = 0;
	virtual bool CALL getSuspend() = 0;
	virtual bool CALL getHideMouse() = 0;
	virtual MyCallback CALL getFrameFunc() = 0;
	virtual MyCallback CALL getRenderFunc() = 0;
	virtual MyCallback CALL getFocusGainFunc() = 0;
	virtual MyCallback CALL getFocusLostFunc() = 0;
	virtual MyCallback CALL getExitFunc() = 0;
	virtual HWND CALL getHWND() = 0;
	virtual HWND CALL getParent() = 0;
	virtual int CALL getScreenWidth() = 0;
	virtual int CALL getScreenHeight() = 0;
	// virtual int CALL getScreenBPP() = 0;
	virtual int CALL getfps() = 0;
	virtual const char* CALL getIcon() = 0;
	virtual const char* CALL getTitle() = 0;
	virtual const char* CALL getIniFile() = 0;
	virtual const char* CALL getLogFile() = 0;
	virtual char* CALL getLog() = 0;
	virtual void CALL Log(const char* format, ...) = 0;
	virtual bool CALL launch(const char* url) = 0;
	virtual void CALL savePNG(const char* filename = 0) = 0;
	virtual char* CALL makePath(const char* filename = 0) = 0;
	virtual char* CALL enumFiles(const char* wildcard = 0) = 0;
	virtual char* CALL enumFolders(const char* wildcard = 0) = 0;
	virtual void CALL setIni(const char* section, const char* name, int value) = 0;
	virtual void CALL setIni(const char* section, const char* name, float value) = 0;
	virtual void CALL setIni(const char* section, const char* name, const char* value) = 0;
	virtual int CALL ini_i(const char* section, const char* name, int def_val) = 0;
	virtual float CALL ini_f(const char* section, const char* name, float def_val) = 0;
	virtual char* CALL ini_c(const char* section, const char* name, const char* def_val) = 0;
	virtual void CALL randSeed(int seed = 0) = 0;
	virtual int	CALL rand_i(int min, int max) = 0;
	virtual float CALL rand_f(float min, float max) = 0;
	virtual float CALL getTime() = 0;
	virtual float CALL getDelta() = 0;
	virtual int	  CALL getFPS() = 0;
	virtual void  CALL setMouse(float x, float y) = 0;
	virtual void  CALL getMouse(float* x, float* y) = 0;
	virtual float CALL getMouseX() = 0;
	virtual float CALL getMouseY() = 0;
	virtual int	  CALL getWheel() = 0;
	virtual bool  CALL getMouseOver() = 0;
	virtual bool  CALL getKeyDown(int key) = 0;
	virtual bool  CALL getKeyUp(int key) = 0;
	virtual bool  CALL getKeyState(int key) = 0;
	virtual const char* CALL getKeyName(int key) = 0;
	virtual int	  CALL getKey() = 0;
	virtual int	  CALL GetKeyChr() = 0;
	virtual bool  CALL getKeyEvent(InputEvent* event) = 0;
	virtual bool CALL gfxBegin(Target targ = 0) = 0;
	virtual void CALL gfxEnd() = 0;
	virtual void CALL gfxClear(DWORD color) = 0;
	virtual void CALL gfxLine(float x1, float y1, float x2, float y2, DWORD color = 0xFFFFFFFF, float z = 0.5f) = 0;
	virtual void CALL gfxQuad(const Vertex4* quad, IDirect3DTexture9* tex) = 0;

	virtual void	CALL Texture_Free(IDirect3DTexture9* tex) = 0;
	// 	virtual TEXTURE	CALL Texture_Create(int width, int height) = 0;
	// 	virtual TEXTURE	CALL Texture_Load(const char *filename, DWORD size = 0, bool bMipmap = false) = 0;
	// 	virtual int		CALL Texture_GetWidth(TEXTURE tex, bool bOriginal = false) = 0;
	// 	virtual int		CALL Texture_GetHeight(TEXTURE tex, bool bOriginal = false) = 0;
	// 	virtual DWORD*	CALL Texture_Lock(TEXTURE tex, bool bReadOnly = true, int left = 0, int top = 0, int width = 0, int height = 0) = 0;
	// 	virtual void	CALL Texture_Unlock(TEXTURE tex) = 0;

};

extern "C" { EXPORT My2d* CALL create(int ver); }